﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PI_CS_Network_Controller
{
    class PicsNetworkModel
    {
        List<Link> _links;
        Dictionary<string, SwitchingMatrix> _nodes;

        public PicsNetworkModel()
        {
            _nodes = new Dictionary<string, SwitchingMatrix>();
            _links = new List<Link>();

            CreateTopology();
        }

        public void AddNewNode(string _nodeId, MatrixParameters _parameters)
        {
            _nodes.Add(_nodeId, new SwitchingMatrix(_nodeId, _parameters));
        }

        public bool AddNewLink(string _nodeA, string _nodeB, LinkParameters _parameters)
        {
            foreach (Link _link in _links)
            {
                if ((_link.EndA == _nodeA && _link.EndB == _nodeB))
                {
                    _links[_links.IndexOf(_link)] = new Link(_nodeA, _nodeB, _parameters);
                    return false;
                }
            }
            _links.Add(new Link(_nodeA, _nodeB, _parameters));
            return true;
        }

        public SwitchingMatrix GetNode(string _nodeId)
        {
            return _nodes[_nodeId];
        }

        public List<Link> Links
        {
            get { return _links; }
            set { _links = value; }
        }

        public Dictionary<string, SwitchingMatrix> Nodes
        {
            get { return _nodes; }
            set { _nodes = value; }
        }

        public Route FindOptimalRoute(string _initialNodeId, string _dstNodeId, List<Link> _validLinks, LinkParameters _maxAdditiveParameters)
        {
            Dictionary<string, Route> _minWeightRoutes = new Dictionary<string, Route>();
           
            //List<SwitchingMatrix> _handledNodes = new List<SwitchingMatrix>();

            foreach (string _nodeId in _nodes.Keys)
            {
                _minWeightRoutes.Add(_nodeId, new Route(int.MaxValue));
            }

            string _currentNodeId = _initialNodeId;

            Route _currentRoute = new Route(0);

            _minWeightRoutes = ProcessNeighbors(_currentNodeId, _currentRoute, _validLinks, _minWeightRoutes, _dstNodeId, _maxAdditiveParameters);

            return _minWeightRoutes[_dstNodeId];
            /*
            _shortestRoutes[_initialNode].Add() WeightSum = _initialNode.Weight; 

            while (_handledNodes.Count != _nodes.Count)
            {
                List<SwitchingMatrix> _closestNodes = (List<SwitchingMatrix>)(from s in _shortestRoutes orderby s.Value.WeightSum select s.Key).ToList();

                SwitchingMatrix _currentNode = null;
                
                foreach (SwitchingMatrix _node in _closestNodes)
                {
                    if (!_handledNodes.Contains(_node))
                    {
                        if (_shortestRoutes[_node].WeightSum == int.MaxValue)
                            return _shortestRoutes;
                        _currentNode = _node;
                        break;
                    }
                }
                

                foreach (Link _link in _currentLinks)
                {
                    if (_shortestRoutes[_link.EndB].WeightSum > _link.Weight + _link.EndB.Weight + _shortestRoutes[_link.EndA].WeightSum)
                    {
                        _shortestRoutes[_link.EndB].Links = _shortestRoutes[_link.EndA].Links.ToList();
                        _shortestRoutes[_link.EndB].Links.Add(_link);
                        _shortestRoutes[_link.EndB].WeightSum = _link.Weight + _link.EndB.Weight + _shortestRoutes[_link.EndA].WeightSum;
                    }
                }

                _handledNodes.Add(_currentNode);
                
 
            }
                 */

        }

        public Dictionary<string, Route> ProcessNeighbors(string _currentNodeId, Route _currentRoute, List<Link> _validLinks, Dictionary<string, Route> _minWeightRoutes, 
            string _dstNodeId, LinkParameters _maxAdditiveParameters)
        {
            var _currentLinks = from c in _validLinks where c.EndA == _currentNodeId select c;

            List<Link> _currentLinksList = _currentLinks.ToList();

            List<Link> _currentLinksSorted = _currentLinksList.OrderBy(x => (int)(x.Weight + _nodes[x.EndB].Weight)).ToList();

            foreach (Link _currentLink in _currentLinksSorted)
            {
                bool _visitedNode = false;
                foreach (Link _routeLink in _currentRoute.Links)
                {
                    if (_routeLink.EndA == _currentLink.EndB || _routeLink.EndB == _currentLink.EndB)
                    {
                        _visitedNode = true; 
                        break;
                    }
                }
                if (!_visitedNode && _minWeightRoutes[_dstNodeId].WeightSum > _currentRoute.WeightSum + _currentLink.Weight + _nodes[_currentLink.EndB].Weight
                    && _currentRoute.AdditiveParams.Delay + _currentLink.Parameters.Delay + _nodes[_currentLink.EndB].Parameters.Delay < _maxAdditiveParameters.Delay)
                {
                    if (_currentRoute.WeightSum + _currentLink.Weight + _nodes[_currentLink.EndB].Weight < _minWeightRoutes[_currentLink.EndB].WeightSum)
                    {
                        Route _nextCurrentRoute = new Route(_currentRoute);
                        _nextCurrentRoute.AddLink(_currentLink, _nodes[_currentLink.EndB]);
                        _minWeightRoutes[_currentLink.EndB] = _nextCurrentRoute;
                        if (_currentLink.EndB != _dstNodeId)
                            ProcessNeighbors(_currentLink.EndB, _nextCurrentRoute, _validLinks, _minWeightRoutes, _dstNodeId, _maxAdditiveParameters);
                    }
                    else if (_currentRoute.AdditiveParams.Delay + _currentLink.Parameters.Delay + _nodes[_currentLink.EndB].Parameters.Delay < _minWeightRoutes[_currentLink.EndB].AdditiveParams.Delay)
                    {
                        Route _nextCurrentRoute = new Route(_currentRoute);
                        _nextCurrentRoute.AddLink(_currentLink, _nodes[_currentLink.EndB]);
                        if (_currentLink.EndB != _dstNodeId)
                            ProcessNeighbors(_currentLink.EndB, _nextCurrentRoute, _validLinks, _minWeightRoutes, _dstNodeId, _maxAdditiveParameters);
                    }
                }
            }
            return _minWeightRoutes;
        }

        public void CreateTopology()
        {
            AddNewNode("1", new MatrixParameters(10));
            AddNewNode("2", new MatrixParameters(20));
            AddNewNode("3", new MatrixParameters(10));
            AddNewNode("4", new MatrixParameters(10));
            AddNewNode("5", new MatrixParameters(20));
            AddNewNode("6", new MatrixParameters(60));
            AddNewNode("7", new MatrixParameters(30));
            AddNewNode("8", new MatrixParameters(30));

            AddNewLink("1", "4", new LinkParameters(2000, 60, 20));
            AddNewLink("4", "1", new LinkParameters(2000, 60, 20));
            AddNewLink("1", "7", new LinkParameters(3000, 20, 20));
            AddNewLink("7", "1", new LinkParameters(3000, 20, 20));
            AddNewLink("7", "4", new LinkParameters(3000, 20, 30));
            AddNewLink("4", "7", new LinkParameters(3000, 20, 30));
            AddNewLink("4", "2", new LinkParameters(1000, 10, 60));
            AddNewLink("2", "4", new LinkParameters(1000, 10, 60));
            AddNewLink("4", "6", new LinkParameters(3000, 10, 30));
            AddNewLink("6", "4", new LinkParameters(3000, 10, 30));
            AddNewLink("6", "8", new LinkParameters(1000, 20, 30));
            AddNewLink("8", "6", new LinkParameters(1000, 20, 30));
            AddNewLink("2", "8", new LinkParameters(3000, 10, 30));
            AddNewLink("8", "2", new LinkParameters(3000, 10, 30));
            AddNewLink("2", "5", new LinkParameters(2000, 50, 40));
            AddNewLink("5", "2", new LinkParameters(2000, 50, 40));
            AddNewLink("1", "5", new LinkParameters(2000, 30, 10));
            AddNewLink("5", "1", new LinkParameters(2000, 30, 10));
            AddNewLink("5", "3", new LinkParameters(1000, 20, 20));
            AddNewLink("3", "5", new LinkParameters(1000, 20, 20));
            AddNewLink("3", "8", new LinkParameters(3000, 10, 10));
            AddNewLink("8", "3", new LinkParameters(3000, 10, 10));
            AddNewLink("6", "7", new LinkParameters(2000, 30, 20));
            AddNewLink("7", "6", new LinkParameters(2000, 30, 20));
        }
    }
}
